import mx.events.EventDispatcher;
import mx.utils.Delegate;

import org.molamil.acto.AppConf;
import org.molamil.acto.AppParseConfVO;
import org.molamil.acto.AppParser;
import org.molamil.acto.commands.CommandContext;
import org.molamil.acto.commands.CommandMaster;
import org.molamil.acto.commands.CommandsConf;
import org.molamil.acto.commands.controller.AbstractCommandsController;
import org.molamil.acto.commands.controller.CommandsController;
import org.molamil.acto.core.EventConstants;
import org.molamil.acto.core.ExternalConfProvider;
import org.molamil.acto.core.Logger;
import org.molamil.acto.pages.MovieContext;
import org.molamil.acto.pages.PageContext;
import org.molamil.acto.pages.PagesConf;
import org.molamil.acto.pages.controller.AbstractPagesController;
import org.molamil.acto.pages.controller.PagesController;
import org.molamil.acto.pages.factory.MovieContainer;
import org.molamil.acto.pages.factory.PageContainer;
import org.molamil.acto.pages.history.HistoryStack;
import org.molamil.acto.pages.master.MovieMaster;
import org.molamil.acto.pages.master.PageMaster;
import org.molamil.acto.pages.preload.PagePinger;
import org.molamil.acto.pages.preload.PagePreloader;
import org.molamil.acto.props.BasicPropContainer;
import org.molamil.acto.props.PropProvider;
import org.molamil.commons.EventUtil;
import org.molamil.commons.ObjectUtil;
import org.molamil.acto.core.AppRole;
import org.molamil.acto.roles.Role;
import org.molamil.acto.props.PropContainer;


/**
 * XXX: Document.
 * 
 * @author Jorge Hernandez
 */
class org.molamil.acto.App implements CommandsController, PagesController, PageContainer, MovieContainer, 
									  ExternalConfProvider, PagePreloader, PagePinger,
									  PropProvider {
	
	
	// Properties
	
	private static var log:Logger = Logger.getLogger("App");
	
	private var pagesController:AbstractPagesController;	private var commandsController:AbstractCommandsController;	private var propContainer:PropContainer;	private var isPagesConfLoad:Boolean;	private var isCommandsConfLoad:Boolean;
	private var xmlConf:XML;
	private var xmlConfLoadDelegate:Function;
	private var pagesConfLoadDelegate:Function;	private var commandsConfLoadDelegate:Function;
	
	
	// Constructor
	
	public function App() {
		
		log.debug("Constructor");
		
		EventDispatcher.initialize(this);
		
		propContainer = new BasicPropContainer();
		
		isPagesConfLoad = false;
		isCommandsConfLoad = false;
		
		// Pages.
		
		var pagesControllerClass:Function = AppConf.pagesControllerClass;
		pagesController = new pagesControllerClass();
		
		EventUtil.propagate(pagesController, this, EventConstants.MC_STATE_CHANGE);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_REQUEST);		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_DISPLAY);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_CLEAR);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_IN);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_ON);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_OUT);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_OFF);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_LOAD_START);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_LOAD_PROGRESS);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_LOAD_INIT);
		EventUtil.propagate(pagesController, this, EventConstants.MOVIE_LOAD_ERROR);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_REQUEST);		EventUtil.propagate(pagesController, this, EventConstants.PAGE_DISPLAY);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_CLEAR);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_IN);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_ON);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_OUT);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_OFF);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_LOAD_START);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_LOAD_PROGRESS);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_LOAD_INIT);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_LOAD_ERROR);		EventUtil.propagate(pagesController, this, EventConstants.PAGES_CONF_LOAD);		EventUtil.propagate(pagesController, this, EventConstants.CONF_LOAD_ERROR);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_PING_START);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_PING_COMPLETE);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_PING_ERROR);
		EventUtil.propagate(pagesController, this, EventConstants.PAGES_PING_START);
		EventUtil.propagate(pagesController, this, EventConstants.PAGES_PING_COMPLETE);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_PRELOAD_START);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_PRELOAD_PROGRESS);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_PRELOAD_INIT);
		EventUtil.propagate(pagesController, this, EventConstants.PAGE_PRELOAD_ERROR);
		EventUtil.propagate(pagesController, this, EventConstants.PAGES_PRELOAD_START);
		EventUtil.propagate(pagesController, this, EventConstants.PAGES_PRELOAD_PROGRESS);
		EventUtil.propagate(pagesController, this, EventConstants.PAGES_PRELOAD_INIT);
		EventUtil.propagate(pagesController, this, EventConstants.PAGES_PRELOAD_STOP);
		EventUtil.propagate(pagesController, this, EventConstants.PAGES_PRELOAD_RESTART);		EventUtil.propagate(pagesController, this, EventConstants.HISTORY_BACK);		EventUtil.propagate(pagesController, this, EventConstants.HISTORY_FORWARD);		EventUtil.propagate(pagesController, this, EventConstants.ROLE_REQUIRED_ERROR);
		
		pagesConfLoadDelegate = Delegate.create(this, onPagesConfLoadListener);
		pagesController.addEventListener(EventConstants.PAGES_CONF_LOAD, pagesConfLoadDelegate);
		
		// Commands.
		
		var commandsControllerClass:Function = AppConf.commandsControllerClass;
		commandsController = new commandsControllerClass();
		
		EventUtil.propagate(commandsController, this, EventConstants.COMMAND_REQUEST);		EventUtil.propagate(commandsController, this, EventConstants.COMMAND_EXECUTE_START);		EventUtil.propagate(commandsController, this, EventConstants.COMMAND_EXECUTE_COMPLETE);		EventUtil.propagate(commandsController, this, EventConstants.COMMAND_EXECUTE_ERROR);		EventUtil.propagate(commandsController, this, EventConstants.COMMANDS_CONF_LOAD);		EventUtil.propagate(commandsController, this, EventConstants.ROLE_REQUIRED_ERROR);
		
		commandsConfLoadDelegate = Delegate.create(this, onCommandsConfLoadListener);
		commandsController.addEventListener(EventConstants.COMMANDS_CONF_LOAD, commandsConfLoadDelegate);
			
	}
	
	
	// Functions
	
	public function dispatchEvent(eventObj:Object):Void {}
	public function addEventListener(event:String, handler):Void {}
	public function removeEventListener(event:String, handler):Void {}
	
	public function loadConf(source:Object):Void {
		log.debug("loadConf(), source = " + source);
		xmlConfLoadDelegate = Delegate.create(this, onXmlConfLoadListener);
		xmlConf = new XML();
		xmlConf.ignoreWhite = true;
		xmlConf.onLoad = xmlConfLoadDelegate;
		xmlConf.load(String(source));
	}
	
	public function getPage(name:String):PageContext {
		return pagesController.getPage(name);
	}
	
	public function getPages():Array {
		return pagesController.getPages();
	}
	
	public function getPagesAt(level:Number):Array {
		return pagesController.getPagesAt(level);
	}
	
	public function containsPage(name:String):Boolean {
		return pagesController.containsPage(name);
	}
	
	public function getMovie(name:String):MovieContext {
		return pagesController.getMovie(name);
	}
	
	public function getMovies():Array {
		return pagesController.getMovies();
	}
	
	public function containsMovie(name:String):Boolean {
		return pagesController.containsMovie(name);
	}
	
	public function goto(name:String, params:Object):PageMaster {
		return pagesController.goto(name, params);
	}

	public function reloadPage():Void {
		pagesController.reloadPage();
	}

	public function getDisplayedPage():PageMaster {
		return pagesController.getDisplayedPage();
	}
	
	public function getCurrentPage():PageMaster {
		return pagesController.getCurrentPage();
	}
	
	public function displayMovie(name:String, params:Object):MovieMaster {
		return pagesController.displayMovie(name, params);
	}
	
	public function displayMovieForPage(name:String, params:Object):MovieMaster {
		return pagesController.displayMovieForPage(name, params);
	}
	
	public function clearMovie(name:String):Void {
		pagesController.clearMovie(name);
	}
	
	public function getDisplayedMovie(name:String):MovieMaster {
		return pagesController.getDisplayedMovie(name);
	}
	
	public function getDisplayedMovies():Array {
		return pagesController.getDisplayedMovies();
	}
	
	public function isDisplayedMovie(name:String):Boolean {
		return getDisplayedMovie(name) != null;
	}

	public function preload():Void {
		PagePreloader(pagesController).preload();
	}

	public function stopPreload():Void {
		PagePreloader(pagesController).stopPreload();
	}

	public function restartPreload():Void {
		PagePreloader(pagesController).restartPreload();
	}

	public function ping():Void {
		PagePinger(pagesController).ping();
	}

	public function stopPing():Void {
		PagePinger(pagesController).stopPing();
	}
	
	public function back():PageMaster {
		return pagesController.back();
	}
	
	public function forward():PageMaster {
		return pagesController.forward();
	}
	
	public function getHistory():HistoryStack {
		return pagesController.getHistory();
	}
	
	public function getProp(name:String):Object {
		return propContainer.getProp(name);
	}
	
	public function execute(name:String, params:Object):CommandMaster {
		return commandsController.execute(name, params);
	}
	
	public function containsCommand(name:String):Boolean {
		return commandsController.containsCommand(name);
	}
	
	public function getCommand(name:String):CommandContext {
		return commandsController.getCommand(name);
	}
	
	public function getCommands():Array {
		return commandsController.getCommands();
	}
	
	public function addCmdListener(name:String, event:String, handler):Void {
		commandsController.addCmdListener(name, event, handler);
	}
	
	public function removeCmdListener(name:String, event:String, handler):Void {
		commandsController.removeCmdListener(name, event, handler);
	}
	
	public function getRole():Role {
		return AppRole.getInstance().currentRole;
	}
	
	public function setRole(role:Role):Void {
		AppRole.getInstance().currentRole = role;
	}

	private function onXmlConfLoadListener(success:Boolean):Void {
		
		log.debug("onXmlConfLoadListener(), success = " + success);
		
		if (success) {
			
			var appConfVO:AppParseConfVO = AppParser.parse(xmlConf);
			
			// Merge conf properties from the app confs in the XML.
			ObjectUtil.mergeTyped(AppConf, appConfVO.props.app);			ObjectUtil.mergeTyped(PagesConf, appConfVO.props.pages);			ObjectUtil.mergeTyped(CommandsConf, appConfVO.props.commands);
			
			// Set the props in the container.
			for (var p:String in appConfVO.props)
				propContainer.setProp(p, appConfVO.props[p]);
			
			// Load/Parse the Pages XML conf.
			if (appConfVO.pagesXml != null) {
				ExternalConfProvider(pagesController).loadConf(appConfVO.pagesXml);
			} else {
				onPagesConfLoadListener(null);
			}
			
			// Load/Parse the Commands XML conf.	
			if (appConfVO.commandsXml != null) {				ExternalConfProvider(commandsController).loadConf(appConfVO.commandsXml);
			} else {
				onCommandsConfLoadListener(null);
			}
				
			log.info("Conf loaded");
			
		} else {
			
			log.severe("Error when loading conf");
			
			var eventObject:Object = {target: this, type: EventConstants.CONF_LOAD_ERROR};
			dispatchEvent(eventObject);
			
		}
		
	}
	
	private function onPagesConfLoadListener(eventObject:Object):Void {
		isPagesConfLoad = true;
		if (isCommandsConfLoad) {
			var eventObject:Object = {target: this, type: EventConstants.CONF_LOAD};
			dispatchEvent(eventObject);
		}
	}
	
	private function onCommandsConfLoadListener(eventObject:Object):Void {
		isCommandsConfLoad = true;
		if (isPagesConfLoad) {
			var eventObject:Object = {target: this, type: EventConstants.CONF_LOAD};
			dispatchEvent(eventObject);
		}
	}
	
	
}